home *** CD-ROM | disk | FTP | other *** search
- /* Library to interface to the Resource Manager.
-
- Revision History:
-
- 94/01/09 aih
- - removed CATCH handler from ResFileExists
-
- 93/12/05 aih
- - fixed error in call to blockmove that could move a dereferenced handle
-
- 93/11/20 aih
- - added function to get an indexed resource
-
- 93/10/27 aih
- - fixed off-by-one error in ResStrLen
-
- 93/03/12 AIH
- - Updated memory limiting
-
- 92/03/03 AIH
- - Added function to get a resource by name
-
- 91/06/01 AIH
- - Added function to get a resource from the application's resource file
-
- 91/05/31 AIH
- - While browsing through the UMPG I discovered that 'actb' and 'dctb'
- resources aren't returned by GetResource due to some weird patch
- to the Resource Manager which was supposed to fix a problem
- in the Dialog Manager (why didn't Apple just patch the Dialog
- Manager?). GetResource returns a handle to a copy of the actual
- 'actb' or 'dctb' resource. Since nearly all of my code loads
- resources by calling the function "ResGet" in this file, I applied
- the un-bug-fix to this function, so that ResGet returns a handle
- to the actual resource, not a copy of the resource.
- - Added function to release a resource
-
- 91/05/06 AIH
- - Fixed a problem with saving the value of ResLoad, which was caused by casting
- a short into a Boolean
-
- 91/04/21-23 AIH
- - Added functions to read strings up to a maximum length
- - Aliases are resolved before a file is opened
-
- 91/04/18 AIH
- - The function for verifying the sanity of a resource fork is no longer
- used since I'm not sure that it's 32-bit clean (there's a mask using
- the low three bytes of something that looks like a pointer)
-
- 91/04/09 AIH
- - Added function to replace an existing resource
- - More careful about use of ResLoad (added assertions to check it) and
- more careful about purgeable resources
- - Memory is limited before resources are loaded so that memory reserves
- won't be tapped
-
- 91/04/01-02 AIH
- - Finished adapting the function for testing the sanity of a resource fork
- - Changed use of ReallocHandle to SetHandleSize
-
- 91/03/23 AIH
- - Found TN#232, which explained why we must call StripAddress before
- calling OpenResFile (and by extension HOpenResFile). Incorporated this
- bug fix, which should fix the very odd problem TMON was reporting whenever
- OpenResFile was called with trap discipline enabled.
-
- 91/03/17 AIH
- - Removed 'Get' suffix from most resource functions
-
- 91/03/03 AIH
- - Shortened prefix of resource library functions from "Rsrc" to "Res"
- and shortened the names of several functions
-
- 91/02/27 AIH
- - Added resource fork sanity checker written by John Norstad for
- Disinfectant
-
- 91/01/20 AIH
- - Changed calls from memmove to BlockMove
-
- 91/01/05 Ari Halberstadt (AIH)
- - Inserted this standard header in all files */
-
- #include <string.h>
- #include "LowMemLib.h"
- #include "MathLib.h"
- #include "MacLib.h"
- #include "MemoryLib.h"
- #include "StringLib.h"
- #include "ResourceLib.h"
-
- /* true if a valid resource */
- Boolean ResValid(Handle rsrc)
- {
- return(HandleValid(rsrc) && HandleIsResource(rsrc));
- }
-
- /* true if the resource file exists */
- Boolean ResFileExists(FileType *fp)
- {
- CInfoPBRec pb;
- Boolean exists = false;
-
- if (FileExists(fp)) {
- FileCatalog(fp, &pb);
- exists = (pb.hFileInfo.ioFlRLgLen > 0);
- }
- return(exists);
- }
-
- /* true if the resource file is already open */
- Boolean ResFileIsOpen(FileType *fp)
- {
- return(false); // program_note: to do
- }
-
- /* open the resource file */
- FileRefType ResFileOpen(FileType *fp, FilePermType permission)
- {
- FileRefType ref = FILE_CLOSED;
-
- require(! ResFileIsOpen(fp));
- FileResolve(fp);
- ResFileValid(fp);
- ref = HOpenResFile(fp->vol, fp->dir,
- (StringPtr) StripAddress((Ptr) fp->pnm), permission);
- FailResError();
- ensure(ref != FILE_CLOSED);
- return(ref);
- }
-
- /* close the resource file */
- void ResFileClose(FileRefType ref)
- {
- if (ref != FILE_CLOSED) {
- CloseResFile(ref);
- FailResError();
- }
- }
-
- /* create the resource file */
- void ResFileCreate(FileType *fp)
- {
- require(FileValid(fp));
- HCreateResFile(fp->vol, fp->dir, fp->pnm);
- FailResError();
- ensure(ResFileValid(fp));
- }
-
- /* true if the resource exists */
- Boolean ResExists(ResType type, short id)
- {
- volatile Boolean load = false;
- Boolean exists = false;
-
- TRY {
- load = GetResLoad();
- SetResLoad(false);
- exists = (GetResource(type, id) != NULL && ! ResError());
- } CLEANUP {
- SetResLoad(load);
- } ENDTRY;
- return(exists);
- }
-
- /* true if the resource exists in the current resource file */
- Boolean ResExists1(ResType type, short id)
- {
- volatile Boolean load = false;
- Boolean exists = false;
-
- TRY {
- load = GetResLoad();
- SetResLoad(false);
- exists = (Get1Resource(type, id) != NULL && ! ResError());
- } CLEANUP {
- SetResLoad(load);
- } ENDTRY;
- return(exists);
- }
-
- /* get the resource, making sure that there's enough memory */
- static void DoResGet(Handle rsrc)
- {
- size_t size = 0;
-
- /* Turned off resource loading so that when the patch to GetResource
- calls HandToHand it will be passed a nil handle, causing it
- to set a nilHandleErr and return the original handle. We also
- turned of resource loading so we could check the memory situation. */
- require(GetResLoad() == false);
- FailNILRes(rsrc);
- if (! *rsrc) {
- /* resource isn't already in memory */
- size = SizeResource(rsrc);
- FailResError();
- MemCheck(size);
- SetResLoad(true);
- LoadResource(rsrc);
- FailResError();
- }
- SetResLoad(true);
- ensure(GetResLoad());
- ensure(ResValid(rsrc));
- }
-
- /* get the resource */
- Handle ResGet(ResType type, short id)
- {
- Handle rsrc = NULL;
-
- require(GetResLoad());
- TRY {
- SetResLoad(false);
- rsrc = GetResource(type, id);
- DoResGet(rsrc);
- } CATCH {
- SetResLoad(true);
- } ENDTRY;
- return(rsrc);
- }
-
- /* get the resource in the current resource file only */
- Handle ResGet1(ResType type, short id)
- {
- Handle rsrc = NULL;
-
- require(GetResLoad());
- TRY {
- SetResLoad(false);
- rsrc = Get1Resource(type, id);
- DoResGet(rsrc);
- } CATCH {
- SetResLoad(true);
- } ENDTRY;
- return(rsrc);
- }
-
- /* get the named resource */
- Handle ResGetNamed(ResType type, const CStr255 name)
- {
- Str255 pname;
- Handle rsrc = NULL;
-
- require(GetResLoad());
- TRY {
- SetResLoad(false);
- c2pstr(strcpy((char *) pname, name));
- rsrc = GetNamedResource(type, pname);
- DoResGet(rsrc);
- } CATCH {
- SetResLoad(true);
- } ENDTRY;
- return(rsrc);
- }
-
- /* get the indexed resource */
- Handle ResGetIndexed(ResType type, short i)
- {
- Handle rsrc = NULL;
-
- require(GetResLoad());
- TRY {
- SetResLoad(false);
- rsrc = GetIndResource(type, i);
- DoResGet(rsrc);
- } CATCH {
- SetResLoad(true);
- } ENDTRY;
- return(rsrc);
- }
-
- /* Get the resource from the application's resource file, regardless
- of the current resource file. */
- Handle ResGetApp(ResType type, short id)
- {
- volatile FileRefType ref = FILE_CLOSED;
- Handle rsrc = NULL;
-
- TRY {
- ref = CurResFile();
- FailResError();
- UseResFile(MacAppRefNum());
- FailResError();
- rsrc = ResGet1(type, id);
- UseResFile(ref);
- FailResError();
- } CATCH {
- if (ref != FILE_CLOSED)
- UseResFile(ref);
- } ENDTRY;
- return(rsrc);
- }
-
- /* Release the resource. Based on notes by John Norstad published with
- the source code to Disinfectant. */
- void ResRelease(Handle rsrc)
- {
- volatile THz zone = NULL;
-
- require(! rsrc || ResValid(rsrc));
- TRY {
- if (rsrc) {
- zone = GetZone(); FailMemError();
- SetZone(HandleZone(rsrc)); FailMemError();
- ReleaseResource(rsrc); FailResError();
- SetZone(zone); FailMemError();
- }
- } CATCH {
- if (zone)
- SetZone(zone);
- } ENDTRY;
- }
-
- /* write the resource */
- void ResWrite(Handle rsrc)
- {
- SignedByte state = 0;
-
- require(ResValid(rsrc));
- state = HandleNoPurge(rsrc);
- ChangedResource(rsrc);
- FailResError();
- WriteResource(rsrc);
- FailResError();
- HandleRestore(rsrc, state);
- }
-
- /* Add and write the resource to the current resource file. You must call
- ResWrite after calling this function. */
- void ResAdd(Handle rsrc, ResType type, short id)
- {
- require(HandleValid(rsrc));
- AddResource(rsrc, type, id, (StringPtr) "");
- FailResError();
- ensure(ResValid(rsrc));
- }
-
- /* set the resource to the handle */
- void ResSet(Handle rsrc, ResType type, short id)
- {
- require(HandleValid(rsrc));
- if (ResExists1(type, id))
- ResRemove(type, id);
- ResAdd(rsrc, type, id);
- ResWrite(rsrc);
- ensure(ResValid(rsrc));
- }
-
- /* remove the specified resource from the current resource file */
- void ResRemove(ResType type, short id)
- {
- Handle rsrc = NULL;
- volatile Boolean load = false;
-
- TRY {
- load = GetResLoad();
- SetResLoad(false);
- rsrc = Get1Resource(type, id);
- FailNILRes(rsrc);
- SetResLoad(load);
- RmveResource(rsrc);
- FailResError();
- } CATCH {
- SetResLoad(load);
- } ENDTRY;
- }
-
- /* Read the first n bytes of the resource with the given type and id
- into the given pointer. */
- void ResPtr(ResType type, short id, void *data, size_t n)
- {
- Handle rsrc = NULL;
-
- require(MemValid(data));
- require(n >= 0);
- rsrc = ResGet(type, id);
- if (SizeResource(rsrc) < n)
- n = SizeResource(rsrc);
- BlockMove(*rsrc, data, n);
- }
-
- /* Set the resource to the first n bytes of the data. If the resource exists
- it is resized to hold the data, otherwise it is created. The resource is set
- in the current resource file only. */
- void ResPtrSet(ResType type, short id, void *data, size_t n)
- {
- volatile Handle rsrc = NULL;
-
- require(MemValid(data));
- require(n >= 0);
- TRY {
- if (ResExists1(type, id)) {
- /* modify existing resource */
- rsrc = ResGet1(type, id);
- HandleSizeSet(rsrc, n);
- }
- else {
- /* add new resource */
- rsrc = HandleBegin(n);
- ResAdd(rsrc, type, id);
- }
- BlockMove(data, *rsrc, n);
- ResWrite(rsrc);
- } CLEANUP {
- HandleEnd(rsrc);
- } ENDTRY;
- }
-
- /* get the 'STR ' resource */
- void ResString(short id, CStr255 str)
- {
- require(StrValid(str, -1));
- *str = 0;
- ResPtr('STR ', id, str, sizeof(CStr255));
- p2cstr((StringPtr) str);
- ensure(StrValid(str, sizeof(CStr255)));
- }
-
- /* get the first 'len' characters of the string */
- void ResStringLen(short id, char *str, short len)
- {
- CStr255 bigstr;
-
- require(len >= 0 && len < sizeof(CStr255));
- ResString(id, bigstr);
- strncpy(str, bigstr, len);
- str[len] = 0;
- ensure(StrValid(str, len));
- }
-
- /* set the 'STR ' resource, adding it if it doesn't already exist */
- void ResStringSet(short id, const CStr255 str)
- {
- Str255 pstr;
-
- require(StrValid(str, sizeof(CStr255)));
- c2pstr(strcpy((char*)pstr, str));
- ResPtrSet('STR ', id, pstr, *pstr + 1);
- }
-
- /* get the n'th string of the 'STR#' resource with the given ID */
- void ResStr(short id, short n, CStr255 str)
- {
- require(n >= 0);
- *str = 0;
- if (n > 0) {
- GetIndString((StringPtr) str, id, n);
- FailResError();
- }
- p2cstr((StringPtr) str);
- ensure(StrValid(str, sizeof(CStr255)));
- }
-
- /* get the first len-1 characters of the string */
- void ResStrLen(short id, short n, char *str, short len)
- {
- CStr255 bigstr;
-
- require(StrValid(str, -1));
- require(0 < len && len <= sizeof(CStr255));
- ResStr(id, n, bigstr);
- strncpy(str, bigstr, len);
- str[len-1] = 0;
- ensure(StrValid(str, len));
- }
-
- /* turn the resource's attribute on or off */
- void ResAttributeSet(Handle rsrc, short attr, Boolean on)
- {
- short attributes;
-
- require(ResValid(rsrc));
- attributes = GetResAttrs(rsrc);
- FailResError();
- if (on)
- attributes |= attr;
- else
- attributes &= ~attr;
- SetResAttrs(rsrc, attributes);
- FailResError();
- }
-
- #ifndef NOT_USED
-
- Boolean ResFileValid(FileType *fp)
- {
- /* this stub will always succeed */
- return(true);
- }
-
- #else /* NOT_USED */
-
- /* This function appears to work, but I don't understand it, and,
- worse, I fear it may not be 32-bit clean. In the interests of
- compatability this function will not be used. */
-
- /* Check a Resource File's Sanity. Adapted from source code to
- Disinfectant, written by John Norstad.
-
- This routine checks the sanity of a resource file by scanning the entire
- resource map. The Resource Manager does not do error checking, and can
- bomb or hang if you use it to open a damaged resource file. This routine
- can be called first to precheck the file. If the resource map is damaged
- mapReadErr is set.
-
- This function appears prone to compatability problems since it examines
- data structures "owned" by the Resource Manager. Also, many of the
- checks are fairly long sequences of logical and's, so I'm not sure
- John wrote this function correctly (I already found a few errors). */
- OSErr ResFileValid(FileType *fp)
- {
- long count; /* number of bytes to read */
- long logEOF; /* logical EOF */
- unsigned long dataLWA; /* offset in file of data end */
- unsigned long mapLWA; /* offset in file of map end */
- unsigned short typeFWA; /* offset from map begin to type list */
- unsigned short nameFWA; /* offset from map begin to name list */
- unsigned char *pType; /* pointer into type list */
- unsigned char *pName; /* pointer to start of name list */
- unsigned char *pMapEnd; /* pointer to end of map */
- short nType; /* number of resource types in map */
- unsigned char *pTypeEnd; /* pointer to end of type list */
- short nRes; /* number of resources of given type */
- unsigned short refFWA; /* offset from type list to ref list */
- unsigned char *pRef; /* pointer into reference list */
- unsigned char *pRefEnd; /* pointer to end of reference list */
- unsigned short resNameFWA; /* offset from name list to resource name */
- unsigned char *pResName; /* pointer to resource name */
- unsigned long resDataFWA; /* offset from data begin to resource data */
- Ptr map = NULL; /* pointer to resource map */
- Boolean mapOK = false; /* true if map is sane */
- struct {
- unsigned long dataFWA; /* offset in file of data */
- unsigned long mapFWA; /* offset in file of map */
- unsigned long dataLen; /* data area length */
- unsigned long mapLen; /* map area length */
- } header;
-
- TRY {
- memclr(&header, sizeof(header));
- if (mapOK) return;
-
- /* open the resource file */
- FileOpenRes(fp, fsRdPerm);
-
- /* get the logical eof */
- logEOF = FileSize(fp);
- if (logEOF == 0) {
- FileClose(fp);
- return;
- }
-
- /* read and validate the resource header */
- count = FileRead(fp, sizeof(header), &header);
- dataLWA = header.dataFWA + header.dataLen;
- mapLWA = header.mapFWA + header.mapLen;
- mapOK = (count == sizeof(header) &&
- header.mapLen > 28 &&
- header.dataFWA < 0x01000000 &&
- header.mapFWA < 0x01000000 &&
- dataLWA <= logEOF &&
- mapLWA <= logEOF &&
- (dataLWA <= header.mapFWA || mapLWA <= header.dataFWA));
- if (! mapOK) FailOSErr(mapReadErr);
-
- /* read the resource map */
- map = PtrBegin(header.mapLen);
- FileSeek(fp, fsFromStart, header.mapFWA);
- count = FileRead(fp, header.mapLen, map);
-
- /* verify the type list and name list offsets */
- typeFWA = *(unsigned short *) (map + 24);
- nameFWA = *(unsigned short *) (map + 26);
- mapOK = (typeFWA == 28 &&
- nameFWA >= typeFWA &&
- nameFWA <= header.mapLen &&
- ! (typeFWA & 1) && ! (nameFWA & 1));
- if (! mapOK) FailOSErr(mapReadErr);
-
- /* verify the type list, reference lists, and name list */
- pType = (unsigned char *) (map + typeFWA);
- pName = (unsigned char *) (map + nameFWA);
- pMapEnd = (unsigned char *) (map + header.mapLen);
- nType = *(short *) pType + 1;
- pType += 2;
- pTypeEnd = pType + (nType << 3);
- mapOK = (pTypeEnd <= pMapEnd);
- if (! mapOK) FailOSErr(mapReadErr);
- while (pType < pTypeEnd) {
- nRes = *(short*)(pType+4) + 1;
- refFWA = *(unsigned short*)(pType+6);
- pRef = (unsigned char *) (map + typeFWA + refFWA);
- pRefEnd = pRef + 12*nRes;
- mapOK = (pRef >= pTypeEnd &&
- pRef < pName &&
- ! (refFWA & 1));
- if (! mapOK) FailOSErr(mapReadErr);
- while (pRef < pRefEnd) {
- resNameFWA = *(unsigned short*)(pRef+2);
- if (resNameFWA != 0xFFFF) {
- pResName = pName + resNameFWA;
- mapOK = (pResName + *pResName < pMapEnd);
- if (! mapOK) FailOSErr(mapReadErr);
- }
- resDataFWA = *(unsigned long *) (pRef+4) & 0x00FFFFFF;
- mapOK = ((header.dataFWA + resDataFWA) < dataLWA);
- if (! mapOK) FailOSErr(mapReadErr);
- pRef += 12;
- }
- pType += 8;
- }
- ensure(mapOK);
- } CLEANUP {
- FileClose(fp);
- PtrEnd(header.mapLen);
- } CATCH {
- if (FailReason() == fnfErr) {
- mapOK = true;
- RETRY;
- }
- } ENDTRY;
- }
-
- #endif /* NOT_USED */
-